﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using MMITDomain;
using MMITDomain.Abstract;
using MMITDomain.Concrete;
using MMITDomain.Entities;
using System.Data.Entity;
using System.Data.Common;
using MMITCore.Logging;
using System.Collections;
using System.Security.Cryptography;
using System.Xml;
using System.IO;
using System.Xml.Serialization;
using System.Text;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
namespace MMITCore.TransactionActions
{
	public class Actions
	{
	   
		SynkronLogger Log = new SynkronLogger("MMITCore");
		Dictionary<string, object> d = new Dictionary<string, object>();
		ResponseObject ro = new ResponseObject();

		/// <summary>
		/// Serialize given object into XmlElement.
		/// </summary>
		/// <param name="transformObject">Input object for serialization.</param>
		/// <returns>Returns serialized XmlElement.</returns>
		#region Serialize given object into stream.
		public XmlElement Serialize(object transformObject)
		{
			XmlElement serializedElement = null;
			try
			{
				MemoryStream memStream = new MemoryStream();
				XmlSerializer serializer = new XmlSerializer(transformObject.GetType());
				serializer.Serialize(memStream, transformObject);
				memStream.Position = 0;
				XmlDocument xmlDoc = new XmlDocument();
				xmlDoc.Load(memStream);
				serializedElement = xmlDoc.DocumentElement;
			}
			catch (Exception SerializeException)
			{
				d.Add("", Log.Desc(SerializeException));
				Log.WriteError(623323222, d );
			}
			return serializedElement;
		}
		#endregion // End - Serialize given object into stream.






		/// <summary>
		/// Deserialize given XmlElement into object.
		/// </summary>
		/// <param name="xmlElement">xmlElement to deserialize.</param>
		/// <param name="tp">Type of resultant deserialized object.</param>
		/// <returns>Returns deserialized object.</returns>
		#region Deserialize given string into object.
		public object Deserialize(XmlElement xmlElement, System.Type tp)
		{
			Object transformedObject = null;
			try
			{
				Stream memStream = StringToStream(xmlElement.OuterXml);
				XmlSerializer serializer = new XmlSerializer(tp);
				transformedObject = serializer.Deserialize(memStream);
			}
			catch (Exception DeserializeException)
			{

			}
			return transformedObject;
		}
		#endregion // End - Deserialize given string into object.
		/// <summary>
		/// Conversion from string to stream.
		/// </summary>
		/// <param name="str">Input string.</param>
		/// <returns>Returns stream.</returns>
		#region Conversion from string to stream.
		public Stream StringToStream(String str)
		{
			MemoryStream memStream = null;
			try
			{
				byte[] buffer = Encoding.UTF8.GetBytes(str);//new byte[str.Length];
				memStream = new MemoryStream(buffer);
			}
			catch (Exception StringToStreamException)
			{
			}
			finally
			{
				memStream.Position = 0;
			}

			return memStream;
		}
		#endregion // End - Conversion from string to stream.

        public IVoucherRepository _voucherRepository { get; set; }
        public Actions(IVoucherRepository voucherRepository)
        {
            _voucherRepository = voucherRepository;
        }

        public Actions() { }
		public Voucher IsVoucherValid(string SerialNo, string PIN)
		{
			try
			{
				IVoucherRepository VoucherRepository;
				VoucherRepository = new EFVoucherRepository();
				var voucher = (from p in VoucherRepository.Vouchers
						   where (p.SerialNo ==  SerialNo && p.PIN == PIN)
						   select p).FirstOrDefault();
				return voucher;
				
			}
			catch (Exception e)
			{
				Log.WriteCritical(221, e);
				//v.IsUsed = null;
				Voucher v = new Voucher();
				v.SerialNo = "Exception";
				return v;
			}
	
		}

		public Account GetAccount(int UserId)
		{
			IAccountRepository AccountRepository;
			AccountRepository = new EFAccountRepository();

			
			
			var account = (from p in AccountRepository.Accounts
						   where (p.Id == UserId )
						   select p).FirstOrDefault();
			if (account != null)
			{
				d.Clear();
				d.Add("Account", Log.Desc(account));
				Log.WriteInformation(222, "Account Fetched", d);
				

			}
			else
			{
				Log.WriteWarning(299, "Account Not Found");
			}
			return account;
					
		}

        public Account GetAccount(Partner P)
        {
            IAccountRepository AccountRepository;
            AccountRepository = new EFAccountRepository();

            IPartnerRepository PartnerRepository;
            PartnerRepository = new EFPartnerRepository();

            var partner = (from p in PartnerRepository.Partners
                           where (p.Id == P.Id)
                           select p).FirstOrDefault();

            var account = (from p in AccountRepository.Accounts
                           where (p.Name  ==  partner.Name)
                           select p).FirstOrDefault();

            if (account != null)
            {
                d.Clear();
                d.Add("Account", Log.Desc(account));
                Log.WriteInformation(222, "Account Fetched", d);


            }
            else
            {
                Log.WriteWarning(299, "Account Not Found");
            }
            return account;

        }

        public Account GetAccount(Customer c)
        {
            IAccountRepository AccountRepository;
            AccountRepository = new EFAccountRepository();



            var account = (from p in AccountRepository.Accounts
                           where (p.CustomerId == c.Id)
                           select p).FirstOrDefault();
            if (account != null)
            {
                d.Clear();
                d.Add("Account", Log.Desc(account));
                Log.WriteInformation(4564553, "Account Fetched", d);


            }
            else
            {
                Log.WriteWarning(299, "Account Not Found");
            }
            return account;

        }


		public Account GetAccount(string MSISDN)
		{
			IAccountRepository AccountRepository;
			AccountRepository = new EFAccountRepository();

			ICustomerRepository CustomerRepository;
			CustomerRepository = new EFCustomerRepository();
			int  CustomerId = 0;

			  var customer = (from p in  CustomerRepository.Customers
						   where p.Mobile == MSISDN
						   select p).FirstOrDefault();
			  if (customer != null)
			  {
				  CustomerId = customer.Id;
			  }
			
			var account = (from p in AccountRepository.Accounts
						   where (p.CustomerId == CustomerId)
						   select p).FirstOrDefault();

			if (account != null)
			{
				d.Clear();
				d.Add("Account", Log.Desc(account));
				Log.WriteInformation(222, "Account Fetched", d);


			}
			else
			{
				Log.WriteWarning(299, "Account Not Found");
			}
			return account;

		}

        public Customer GetCustomer(string Username)
        {
            ICustomerRepository CustomerRepository;
            CustomerRepository = new EFCustomerRepository();


            var customer = (from p in CustomerRepository.Customers
                            where p.Username == Username
                            select p).FirstOrDefault();

            return customer;

        }

        public Partner  GetPartner(string Username)
        {
            IPartnerRepository PartnerRepository;
            PartnerRepository = new EFPartnerRepository();


            var partner = (from p in PartnerRepository.Partners
                            where p.Name == Username
                            select p).FirstOrDefault();

            return partner;

        }


		public Customer GetCustomer(int CustomerId)
		{
			ICustomerRepository CustomerRepository;
			CustomerRepository = new EFCustomerRepository();
			

			var customer = (from p in CustomerRepository.Customers
                            where p.Id == CustomerId
							select p).FirstOrDefault();

			return customer;
		}

        public Customer GetCustomer(int AccountId, int CustomerId)
        {
            ICustomerRepository CustomerRepository;
            CustomerRepository = new EFCustomerRepository();

            Account a = GetAccount(AccountId);

            var customer = (from p in CustomerRepository.Customers
                            where p.Id == a.CustomerId
                            select p).FirstOrDefault();

            return customer;
        }

		public IEnumerable<Charge> GetCharges(int TransactionTypeId, int? RoleId , int? AccountId)
		{
			// Get Charges allocated to Transaction

			//List<Charge> ListOfCharges = new List<Charge>();

			IChargeRepository ChargesRepository;
			ChargesRepository = new EFChargeRepository();


			var charges = from p in  ChargesRepository.Charges
						  where p.TransactionTypeId == TransactionTypeId
						  select p;


			return charges;
		}

		public List<Rule> GetRules (int TransactionTypeId, int AccountId)
		{
			

			IRuleRepository RulesRepository;
			RulesRepository = new EFRuleRepository();

			ITransactionRoleAccountRepository TransactionRolesAccountsRepository;
			TransactionRolesAccountsRepository = new EFTransactionRoleAccountRepository();

			List<int> AssignedRoles = (from p in TransactionRolesAccountsRepository.TransactionRoleAccounts
									   where p.AccountId == AccountId
									   select p.TransactionRoleId).ToList();

			List<Rule> Rules = (from p in RulesRepository.Rules
						where p.TransactionTypeId == TransactionTypeId && p.AccountId == AccountId
						select p).ToList();


			if (AssignedRoles.Count > 0)
			{
				
				foreach (int r in AssignedRoles)
				{
					IRuleRepository RoleRulesRepository;
					RoleRulesRepository = new EFRuleRepository();

					List<Rule> RoleRules = (from k in RoleRulesRepository.Rules
								   where k.TransactionTypeId == TransactionTypeId && k.TransactionRoleId == r
								   select k).ToList();
					Rules.AddRange(RoleRules);
				}
					  
			}
			return Rules;
			
		}

		public   VelocityLimit  GetTransactionsInWindow( int AccountId,  DateTime CurrentTime)
		{
			VelocityLimit AccountCount; 
			AccountCount =  new VelocityLimit( AccountId, 0);

			ITransactionRepository TransactionRepository;
			TransactionRepository = new EFTransactionRepository();
			
			List<Transaction> TransactionWithinLimit = (from p in TransactionRepository.Transactions
														 where p.SenderId == AccountId  || p.RecipientId == AccountId && p.TimeInitiated >= CurrentTime
														 select p).ToList();

			AccountCount.NumberOfTransactions =TransactionWithinLimit .Count();
			if( AccountCount.NumberOfTransactions > 0)
			{
			TimeSpan ts =  DateTime.Now - (DateTime)TransactionWithinLimit .FirstOrDefault().TimeInitiated;
			AccountCount.WindowinHours = ts.Hours;
			
			}


			return AccountCount;
		}


		public Account AddCustomerAndAccount(Customer c, bool NewCustomer)
		{
			Account   a ;
			ICustomerRepository CustomerRepository;
			CustomerRepository = new EFCustomerRepository();

			IAccountRepository AccountRepository;
			AccountRepository = new EFAccountRepository();

			var cust = from p in CustomerRepository.Customers
					   where p.Mobile == c.Mobile
					   select p;

			if (cust.Count() == 0)
			{

				
					//Save Customer and return Account
					CustomerRepository.SaveCustomer(c);
					a = AddAccount(c);

					d.Clear();

					d.Add("Customer", Log.Desc(c));
					Log.WriteInformation(226, "Created Customer  with MSISDN : " + c.Mobile, d );
					return a;
				
			}
			else
				//  If the Customer Exists,  Fetch his account 
			{
				a = (from p in AccountRepository.Accounts
					 where p.CustomerId == c.Id
					 select p).FirstOrDefault();


				d.Clear();
				d.Add("Account", Log.Desc(a));
				d.Add("Customer", Log.Desc(c));
				Log.WriteInformation(225, "Recipient with MSISDN : " + c.Mobile + " Exists " ,d );
				return a;
			}

				
			
		}

		public Account AddAccount(Customer c)
		{
			IAccountRepository AccountRepository;
			AccountRepository = new EFAccountRepository();

			int countryId = c.CountryId ?? 0;
			//what happens if countryid is null ?
			Country objCountry = new EFCountryRepository().GetCountryById(countryId);


			Account acc = new Account();
			acc.CustomerId = c.Id;
			acc.Name = c.Mobile;
			acc.ActualBalance = 0.00M;
			acc.AvailableBalance = 0.00M;
			acc.Status = (int)AccountStatuses.Active;
			acc.AccountTypeId = (int)AccountType.UnregisteredCustomer;
			acc.CurrencyId = new EFCountryRepository().GetDefaultCurrency(objCountry).Id;

			//Save Account and Return Account Id
			AccountRepository.SaveAccount(acc);

			d.Clear();
			d.Add("Account",Log.Desc( acc));
			Log.WriteInformation(227, "Created Account with Account Id : " + acc.Id.ToString(), d);
			return acc;
					
		}

		public Transaction AddTransaction(Transaction t, List<TransactionCharge> tc = null )
		{
			ITransactionRepository TransactionRepository;
			TransactionRepository = new EFTransactionRepository();

			IAccountRepository AccountRepository;
			AccountRepository = new EFAccountRepository();
			
			Account a ;
			//Add Transaction
			TransactionRepository.SaveTransaction(t);

			//


			switch (t.TransactionTypeId)
			{
				case (int)TransactionType.LoadVoucher:

					a = (from p in AccountRepository.Accounts
						 where p.Id == t.SenderId 
						 select p).FirstOrDefault();


					a.AvailableBalance += t.Amount;
					a.ActualBalance += t.Amount;
					AccountRepository.SaveAccount(a);

					d.Clear();
					d.Add ("Credited Account  : ",Log.Desc( a));
					Log.WriteInformation(600000226, "Account Credited  : " + t.Amount + ",  AccountId: " + t.SenderId.ToString() + "Transaction Type :" + t.TransactionTypeId, d);

					//Set Transaction to completed 
					t.Status = (int)TransactionStatuses.Completed;
					t.TimeFinalised = DateTime.Now;
					t.TimeProcessed = DateTime.Now;
					TransactionRepository.SaveTransaction(t);
					break;

				case (int)TransactionType.P2PUnregistered:
				case (int)TransactionType.P2PRegistered:
					//Fetching the Recipient Account for Crediting
					a = (from p in AccountRepository.Accounts
						 where p.Id == t.RecipientId
						 select p).FirstOrDefault();

					//Crediting the Recipient 
					a.AvailableBalance += t.Amount;
					a.ActualBalance += t.Amount;
					AccountRepository.SaveAccount(a);

					d.Clear();
					d.Add("Credited Account", Log.Desc(a));
					Log.WriteInformation(600000227, "Account Credited  : " + t.Amount + ",  AccountId: " + t.RecipientId.ToString() + "Transaction Type :" + t.TransactionTypeId,d);

					//Fetching the Sender Account for Debiting
					a = (from p in AccountRepository.Accounts
						 where p.Id == t.SenderId
						 select p).FirstOrDefault();

					//Debiting  the Sender 
					a.AvailableBalance -= t.Amount;
					a.ActualBalance -= t.Amount;
					AccountRepository.SaveAccount(a);

					d.Clear();
					d.Add("Debited Account ",Log.Desc( a));
					Log.WriteInformation(600000228, "Account Debited  : " + t.Amount + ",  AccountId: " + t.SenderId.ToString() + "Transaction Type :" + t.TransactionTypeId,d);

					//Set Transaction to Pending 
					t.Status = (int)TransactionStatuses.Pending;                   
					t.TimeProcessed = DateTime.Now;
					TransactionRepository.SaveTransaction(t);
					break;

				case (int)TransactionType.WithdrawMoney :
					//Fetching the Recipient Account fr Crediting
					a = (from p in AccountRepository.Accounts
						 where p.Id == t.RecipientId
						 select p).FirstOrDefault();

					//Debiting  the Sender 
					a.AvailableBalance -= t.Amount;
					a.ActualBalance -= t.Amount;
					AccountRepository.SaveAccount(a);

					d.Clear();
					d.Add("Debited Account ",Log.Desc( a));
					Log.WriteInformation(6000228, "Account Debited  : " + t.Amount + ",  AccountId: " + t.SenderId.ToString() + "Transaction Type :" + t.TransactionTypeId,d);

					break;
                case (int)TransactionType.P2PExternal:

                    //Fetching the Sender Account for Debiting
                    a = (from p in AccountRepository.Accounts
                         where p.Id == t.SenderId
                         select p).FirstOrDefault();

                    //Debiting  the Sender 
                    a.AvailableBalance -= t.Amount;
                    a.ActualBalance -= t.Amount;
                    AccountRepository.SaveAccount(a);

                    d.Clear();
                    d.Add("Debited Account ", Log.Desc(a));
                    Log.WriteInformation(600000228, "Account Debited  : " + t.Amount + ",  AccountId: " + t.SenderId.ToString() + "Transaction Type :" + t.TransactionTypeId, d);

                    //Set Transaction to Completed 
                    t.Status = (int)TransactionStatuses.Completed;
                    t.TimeProcessed = DateTime.Now;
                    TransactionRepository.SaveTransaction(t);
                    break;
				default:

					Log.WriteInformation(600000229, "Unknown Transaction Type : " + t.TransactionTypeId.ToString() + ", Transaction Id : " + t.TransactionTypeId.ToString());
					break;
			}


			d.Clear();
			d.Add("Transaction",Log.Desc( t));
			Log.WriteInformation(228, "Created Transaction   with TransactionId  " + t.Id.ToString(), d);

			if (tc != null )
			{
				foreach (TransactionCharge r in tc) 
				{ 
				
					r.TransactionId = t.Id;
					AddTransactionCharge(r);
					Log.WriteInformation(229, "Created Transaction Charge for  TransactionId  " + t.Id.ToString());
				}

				
			}
		   
			return t;
		}

		public void InvalidateVoucher(MMITDomain.Entities.Voucher v)
		{
		   
			try 
			{

				IVoucherRepository VoucherRepository;
				VoucherRepository = new EFVoucherRepository();

				
			  
				var voucher = (from p in VoucherRepository.Vouchers
							   where (p.SerialNo == v.SerialNo && p.PIN == v.PIN)
							   select p).FirstOrDefault();

				voucher.IsUsed = true;
				voucher.DateUsed = v.DateUsed;
				voucher.TransactionId = v.TransactionId;


			
				VoucherRepository.SaveVoucher(voucher);

			   

				d.Clear();
				d.Add("Voucher",Log.Desc( v));
				Log.WriteInformation(6000929, "Voucher Invalidated" , d);
				
			}
			catch (Exception e)
			{
				
				Log.WriteCritical(903, e);		   
			
			}
		
		
		}

    
        //Add new vouchers into the DB

        public void AddVoucher(MMITDomain.Entities.Voucher v)
        {
            try 
            
            {
                IVoucherRepository VoucherRepository;
                VoucherRepository = new EFVoucherRepository();
                VoucherRepository.SaveVoucher(v);
            }
            catch (Exception e)
            {

                Log.WriteCritical(903, e);

            }
        }

               

		public TransactionCharge AddTransactionCharge(TransactionCharge tc)
		{

			ITransactionChargeRepository TransactionChargeRepository;
			TransactionChargeRepository = new EFTransactionChargeRepository();

			IAccountRepository AccountRepository;
			AccountRepository = new EFAccountRepository();
			


			TransactionChargeRepository.SaveTransactionCharge(tc);

		   Account  a = (from p in AccountRepository.Accounts
					 where p.Id == 1// It should be the MMIT Customer 
					 select p).FirstOrDefault();


		   a.ActualBalance += tc.Amount;
		   a.AvailableBalance += tc.Amount;
		   AccountRepository.SaveAccount(a);
			//Commit to DB

			return tc;


		
		}

		

		public ResponseObject CheckRules(int TransactionTypeId, int AccountId, string Actor, decimal? TransactionValue )
		{
			
			IEnumerable<Rule> rules;
			rules = GetRules(TransactionTypeId, AccountId);
			ro.ResponseCode = (int)ResponseCodes.PassedRules;
			try
			{
				switch (Actor)
				{
					case "Sender"   :
					case "Recipient" :	
					   if (rules.Count() > 0)
					   {
						   
						   Log.WriteInformation(231, "Rules Fetched");
						   foreach (var r in rules)
						   {
							   d.Add("Rule " + r.Id.ToString(), Log.Desc(r));
							   Log.WriteInformation(54344234, rules);
						   }
						 
						  
						   foreach (var r in rules)
						   {
							   
							   // If the rule is a transaction amount limit
							   if (r.TransactionAmount != null)
							   {
								   if (TransactionValue > r.TransactionAmount)
								   {
									   ro.ResponseCode = (int)ResponseCodes.TransactionAmountExceeded;
									   ro.Description = "The Transaction amount is not allowed for this kind of Transaction Type";

									   d.Clear();
									   d.Add("Flouted Rule ", Log.Desc(r));
									   Log.WriteInformation(219, "RuleId = " + r.Id.ToString() + " Flouted ", d);
									   return ro;
									   
								   }
								 
							   }
							   else
							   { 
								   // If the rule does not have a transaction amount limit , 
								   //It will either be a max balance limit
									if (AccountId != 0) 
									{
										Account a = GetAccount(AccountId);
									   
									   if (r.MaxBalance != null)
									   {
										   if (Actor != "Sender")// To Revisit : For Transfer there is no need to check the Sender's balance, since its not from his wallet
										   { 
									   
											   if (r.MaxBalance < (a.AvailableBalance + TransactionValue))
											   {
												   ro.ResponseCode = (int)ResponseCodes.MaxBalanceLimitExceeded;
												   ro.Description = "You will Exceed your Maximum Balance Limit if you Attempt this Transaction";


												   d.Clear();
												   d.Add("Flouted Rule ",Log.Desc( r));
												   Log.WriteInformation(220, "RuleId = " + r.Id.ToString() + " Flouted ", d);
												   return ro;
										  
											  }
										   }
									  }

									   // Min Balance Limit Only Applicable when the Recipient has an Account - Unregistered User Account Type

									   if (r.MinBalance != null)
									   {
										   if (Actor == "Sender" && TransactionTypeId != (int)TransactionType.LoadVoucher )//To Revisit : For Transfer there is no need to check the Sender's balance, since its not from his wallet
										   {

											   if (r.MinBalance > a.AvailableBalance - TransactionValue)
											   {
												   ro.ResponseCode = (int)ResponseCodes.MinBalanceLimitExceeded;
												   ro.Description = "You will Exceed your Minimum Balance Limit if you Attempt this Transaction";


												   d.Clear();
												   d.Add("Flouted Rule ", Log.Desc(r));
												   Log.WriteInformation(221, "RuleId = " + r.Id.ToString() + " Flouted ", d);
												   return ro;
											   }
										   }
									   }
								  
								   // Velocity Limit
								  
									   if (r.NumberOfTransactions != null && r.WindowPeriodInHours != null)
									   {
										   VelocityLimit vl = GetTransactionsInWindow(AccountId, DateTime.Now.AddHours(-(double)r.WindowPeriodInHours));
									   
										   if (vl.NumberOfTransactions>= r.NumberOfTransactions && vl.WindowinHours <= r.WindowPeriodInHours)
										   {
											   ro.ResponseCode = (int)ResponseCodes.VelocityLimitExceeded;
											   ro.Description = "You can only perform " + r.NumberOfTransactions.ToString() + " within a period of " + r.WindowPeriodInHours.ToString() + " hour(s). Please retry this transaction later "  ;


											   d.Clear();
											   d.Add("Flouted Rule ", Log.Desc(r));
											   Log.WriteInformation(222, "RuleId = " + r.Id.ToString() + " Flouted ", d);
											   Log.WriteInformation(223, vl.NumberOfTransactions.ToString() + "Transactions in the Last " + vl.WindowinHours + " UserId :" + AccountId.ToString(), d);

											   return ro;
										   
										   }
									   }
									}
							   }
						   
						   }
					   }
					   else
					   {
						   
											  
						   Log.WriteWarning(235, "No rules found or Applicable to this transaction set for this Account :"+ AccountId.ToString());

					   }
						break;
					case "New Recipient":

						if (rules.Count() > 0)
						{

							Log.WriteInformation(251, "Rules Fetched");
							Log.WriteInformation(252, rules);
							foreach (var r in rules)
							{

								// If the rule is a transaction amount limit
								if (r.TransactionAmount != null)
								{
									if (TransactionValue > r.TransactionAmount)
									{
										ro.ResponseCode = (int)ResponseCodes.TransactionAmountExceeded;
										ro.Description = "The Transaction amount is not allowed for this kind of Transaction Type";

										d.Clear();
										d.Add("Flouted Rule ", r);
										Log.WriteInformation(253, "RuleId = " + r.Id.ToString() + " Flouted ", d);

									}

								}
								

							}
						}
						else
						{
							Log.WriteWarning(258, "No rules found or Applicable to this transaction set for the Recipient :" + AccountId.ToString());
						}
					   
						break;
					default:
						Log.WriteWarning(258, "Unknown Actor in Transaction, " + Actor);
						ro.ResponseCode = (int)ResponseCodes.SystemError;
						ro.Description = "Please Contact Customer Service";
						break;

				}

				return ro;

			}
			catch (Exception e)
			{
				Log.WriteCritical(902, e);
				ro.ResponseCode = (int)ResponseCodes.SystemError;
				ro.Description = "Please Contact Customer Service";
				return ro;
			}

		}


		

		public Transaction CheckTransaction( string token) 
		{
			ITransactionRepository TransactionRepository;
			TransactionRepository = new EFTransactionRepository();

			Transaction t = (from p in TransactionRepository.Transactions
							 where p.TransactionCode == token
							 select p).FirstOrDefault();
			


			return t; 
		}
		
		public void ChangeTransactionState(long TransactionId, int NewTranactionsStatus, int userid,  int TransactionTypeId)
		{
			//This method does the switching of Statuses of Txns.

			ITransactionRepository TransactionRepository;
			TransactionRepository = new EFTransactionRepository();

			Transaction t = (from p in TransactionRepository.Transactions
							 where p.Id == TransactionId
							 select p).FirstOrDefault();

			

			switch  ( t.TransactionTypeId)  
			{ 
				// In the case of Unregistered customers who want to withdraw their Money. Their transaction code is checked against the original P2P Unregistered Txn. A Withdrawal
				case ((int)TransactionType.P2PUnregistered) :
				case ((int)TransactionType.P2PRegistered):
					Transaction wt = new Transaction();
					wt.RecipientId =t.RecipientId;
					wt.ParentId =t.Id;
					wt.AuthorisationId = userid;
					wt.Amount = t.Amount;
					wt.SenderId = t.RecipientId;
					wt.TransactionCode = t.TransactionCode;
					wt.TransactionTypeId =  (int)TransactionType.WithdrawMoney;
					wt.Status = (int)TransactionStatuses.Completed;
					wt.TimeFinalised = DateTime.Now;
					wt.TimeProcessed = DateTime.Now;
					wt.TimeInitiated = DateTime.Now;
					wt.LastUpdate = DateTime.Now;
					AddTransaction(wt);
					Log.WriteInformation(14242253, "Old Status : " + t.Status.ToString());
					t.Status = (int)TransactionStatuses.Completed;
					TransactionRepository.SaveTransaction(t);
					Log.WriteInformation(1223253, "New Status : " + t.Status.ToString() , d);
					break;
				case ((int)TransactionType.WithdrawMoney):					
					break;
				default :
					break;
		  }
			// transaction updated to show completion
			Log.WriteInformation(144332253, "Old Status : " + t.Status.ToString());
			t.TimeFinalised = DateTime.Now;
			t.TimeProcessed = DateTime.Now;
			t.Status = (int)TransactionStatuses.Completed;
			TransactionRepository.SaveTransaction(t);
			Log.WriteInformation(12983253, "New Status : " + t.Status.ToString(), d);
		   
			d.Clear();
			d.Add("Transaction Status Changed", Log.Desc(t));
			

		}

		public ResponseObject ReserveMoneyandFetchToken( decimal Amount, int UserId , int TransactionTypeId,  string Actor="Sender" )
		{
		   
			ro = CheckRules(TransactionTypeId, UserId, Actor,  Amount);
			if (ro.ResponseCode == 10)
			{
				try
				{
					OTP otp = new OTP();
					Transaction wt = new Transaction();
					wt.RecipientId = UserId; 
					wt.Amount = Amount;
					wt.SenderId = UserId;
					wt.TransactionTypeId = (int)TransactionType.WithdrawMoney;
					wt.Status = (int)TransactionStatuses.Pending;				
					wt.TimeInitiated = DateTime.Now;
					wt.LastUpdate = DateTime.Now;
					wt.TransactionCode = otp.Generate();
					AddTransaction(wt);
					ro.TransactionCode = wt.TransactionCode;
					ro.ResponseCode = (int)ResponseCodes.RequestToken;
					ro.Description = "Withdrawal Token Successfully Generated";
				}
				catch (Exception e)
				{
					ro.ResponseCode = (int)ResponseCodes.SystemError;
					Log.WriteCritical(989221, e);
				}
				
			}

			return ro;


		}

        public List<TransactionCharge> CalculateCharges(int TransactionType, decimal TxnAmount)
        {
            List<TransactionCharge> txncharges = new List<TransactionCharge>();
            IEnumerable<Charge> charges;
            charges = GetCharges(TransactionType, null, null);
            foreach (var c in charges)
            {
                if (c.FixedAmount != null)
                {
                    // If Charge is a Fixed Amount
                    //TotalCharge += (decimal)c.FixedAmount;
                    //Log.WriteInformation(216, "charge for TransactionType=" + TT.ToString() + ", charge value = " + c.FixedAmount.ToString());
                    TransactionCharge t = new TransactionCharge();
                    t.Amount = c.FixedAmount;
                    t.ChargeId = c.Id;
                    t.CreditPartyId = 0;
                    t.Charge = c;
                    t.DebitPartyId = 0;
                    txncharges.Add(t);
                }
                if (c.PercentageAmount != null)
                {
                    //If Charge is a Percentage Amount
                    //TotalCharge += ((decimal)c.PercentageAmount * v.Value) / 100;
                   // Log.WriteInformation(217, "charge for TransactionType=" + TT.ToString() + ", charge value = " + ((c.PercentageAmount * v.Value) / 100).ToString());
                    TransactionCharge t = new TransactionCharge();
                    t.Amount = ((decimal)c.PercentageAmount * TxnAmount) / 100;
                    t.ChargeId = c.Id;
                    t.CreditPartyId = 0;
                    t.Charge = c;
                    t.DebitPartyId = 0;
                    txncharges.Add(t);
                }
            }
          

            return txncharges;
        }

		public static System.Boolean IsNumeric (System.Object Expression)
		{
			if(Expression == null || Expression is DateTime)
				return false;

			if(Expression is Int16 || Expression is Int32 || Expression is Int64 || Expression is Decimal || Expression is Single || Expression is Double || Expression is Boolean)
				return true;
   
			try 
			{
				if(Expression is string)
					Double.Parse(Expression as string);
				else
					Double.Parse(Expression.ToString());
					return true;
				} catch {} // just dismiss errors but return false
				return false;
			}


		}

	public class OTP
	{
		// Define default min and max password lengths.
		private static int DEFAULT_MIN_PASSWORD_LENGTH = 6;
		private static int DEFAULT_MAX_PASSWORD_LENGTH = 6;

		// Define supported password characters divided into groups.
		// You can add (or remove) characters to (from) these groups.
		
		private static string PASSWORD_CHARS_NUMERIC = "123456789";
		

		/// <summary>
		/// Generates a random password.
		/// </summary>
		/// <returns>
		/// Randomly generated password.
		/// </returns>
		/// <remarks>
		/// The length of the generated password will be determined at
		/// random. It will be no shorter than the minimum default and
		/// no longer than maximum default.
		/// </remarks>
		public  string Generate()
		{
			return Generate(DEFAULT_MIN_PASSWORD_LENGTH,
							DEFAULT_MAX_PASSWORD_LENGTH);
		}

		/// <summary>
		/// Generates a random password of the exact length.
		/// </summary>
		/// <param name="length">
		/// Exact password length.
		/// </param>
		/// <returns>
		/// Randomly generated password.
		/// </returns>
		public  string Generate(int length)
		{
			return Generate(length, length);
		}

		/// <summary>
		/// Generates a random password.
		/// </summary>
		/// <param name="minLength">
		/// Minimum password length.
		/// </param>
		/// <param name="maxLength">
		/// Maximum password length.
		/// </param>
		/// <returns>
		/// Randomly generated password.
		/// </returns>
		/// <remarks>
		/// The length of the generated password will be determined at
		/// random and it will fall with the range determined by the
		/// function parameters.
		/// </remarks>
		public  string Generate(int minLength, int maxLength)
		{
			// Make sure that input parameters are valid.
			if (minLength <= 0 || maxLength <= 0 || minLength > maxLength)
				return null;

			// Create a local array containing supported password characters
			// grouped by types. You can remove character groups from this
			// array, but doing so will weaken the password strength.
			char[][] charGroups = new char[][] 
		{
			
			PASSWORD_CHARS_NUMERIC.ToCharArray()
		};

			// Use this array to track the number of unused characters in each
			// character group.
			int[] charsLeftInGroup = new int[charGroups.Length];

			// Initially, all characters in each group are not used.
			for (int i = 0; i < charsLeftInGroup.Length; i++)
				charsLeftInGroup[i] = charGroups[i].Length;

			// Use this array to track (iterate through) unused character groups.
			int[] leftGroupsOrder = new int[charGroups.Length];

			// Initially, all character groups are not used.
			for (int i = 0; i < leftGroupsOrder.Length; i++)
				leftGroupsOrder[i] = i;

			// Because we cannot use the default randomizer, which is based on the
			// current time (it will produce the same "random" number within a
			// second), we will use a random number generator to seed the
			// randomizer.

			// Use a 4-byte array to fill it with random bytes and convert it then
			// to an integer value.
			byte[] randomBytes = new byte[4];

			// Generate 4 random bytes.
			RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
			rng.GetBytes(randomBytes);

			// Convert 4 bytes into a 32-bit integer value.
			int seed = (randomBytes[0] & 0x7f) << 24 |
						randomBytes[1] << 16 |
						randomBytes[2] << 8 |
						randomBytes[3];

			// Now, this is real randomization.
			Random random = new Random(seed);

			// This array will hold password characters.
			char[] password = null;

			// Allocate appropriate memory for the password.
			if (minLength < maxLength)
				password = new char[random.Next(minLength, maxLength + 1)];
			else
				password = new char[minLength];

			// Index of the next character to be added to password.
			int nextCharIdx;

			// Index of the next character group to be processed.
			int nextGroupIdx;

			// Index which will be used to track not processed character groups.
			int nextLeftGroupsOrderIdx;

			// Index of the last non-processed character in a group.
			int lastCharIdx;

			// Index of the last non-processed group.
			int lastLeftGroupsOrderIdx = leftGroupsOrder.Length - 1;

			// Generate password characters one at a time.
			for (int i = 0; i < password.Length; i++)
			{
				// If only one character group remained unprocessed, process it;
				// otherwise, pick a random character group from the unprocessed
				// group list. To allow a special character to appear in the
				// first position, increment the second parameter of the Next
				// function call by one, i.e. lastLeftGroupsOrderIdx + 1.
				if (lastLeftGroupsOrderIdx == 0)
					nextLeftGroupsOrderIdx = 0;
				else
					nextLeftGroupsOrderIdx = random.Next(0,
														 lastLeftGroupsOrderIdx);

				// Get the actual index of the character group, from which we will
				// pick the next character.
				nextGroupIdx = leftGroupsOrder[nextLeftGroupsOrderIdx];

				// Get the index of the last unprocessed characters in this group.
				lastCharIdx = charsLeftInGroup[nextGroupIdx] - 1;

				// If only one unprocessed character is left, pick it; otherwise,
				// get a random character from the unused character list.
				if (lastCharIdx == 0)
					nextCharIdx = 0;
				else
					nextCharIdx = random.Next(0, lastCharIdx + 1);

				// Add this character to the password.
				password[i] = charGroups[nextGroupIdx][nextCharIdx];

				// If we processed the last character in this group, start over.
				if (lastCharIdx == 0)
					charsLeftInGroup[nextGroupIdx] =
											  charGroups[nextGroupIdx].Length;
				// There are more unprocessed characters left.
				else
				{
					// Swap processed character with the last unprocessed character
					// so that we don't pick it until we process all characters in
					// this group.
					if (lastCharIdx != nextCharIdx)
					{
						char temp = charGroups[nextGroupIdx][lastCharIdx];
						charGroups[nextGroupIdx][lastCharIdx] =
									charGroups[nextGroupIdx][nextCharIdx];
						charGroups[nextGroupIdx][nextCharIdx] = temp;
					}
					// Decrement the number of unprocessed characters in
					// this group.
					charsLeftInGroup[nextGroupIdx]--;
				}

				// If we processed the last group, start all over.
				if (lastLeftGroupsOrderIdx == 0)
					lastLeftGroupsOrderIdx = leftGroupsOrder.Length - 1;
				// There are more unprocessed groups left.
				else
				{
					// Swap processed group with the last unprocessed group
					// so that we don't pick it until we process all groups.
					if (lastLeftGroupsOrderIdx != nextLeftGroupsOrderIdx)
					{
						int temp = leftGroupsOrder[lastLeftGroupsOrderIdx];
						leftGroupsOrder[lastLeftGroupsOrderIdx] =
									leftGroupsOrder[nextLeftGroupsOrderIdx];
						leftGroupsOrder[nextLeftGroupsOrderIdx] = temp;
					}
					// Decrement the number of unprocessed groups.
					lastLeftGroupsOrderIdx--;
				}
			}

			// Convert password characters into a string and return the result.
			return new string(password);
		}

        

        
	}

    public class Ancillary
    { 

        
    
    }

 #region Shared 
    public class MMITShared
    {
        public string  OriginatingIP_Port(OperationContext context)
        {
            MessageProperties messageProperties = context.IncomingMessageProperties;
            RemoteEndpointMessageProperty endpointProperty = messageProperties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;

            return endpointProperty.Address + ":" + endpointProperty.Port;
        }
    }

#endregion
}
